﻿using QSocket.Core;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using LogHelper;

namespace QSocket.Server
{
    public class TcpServer : ServerBase
    {
        private static TcpServer s_TcpServer = null;

        private TcpListener m_TcpListener = null;

        private DateTime m_LastClearDate = DateTime.MinValue;

        private Dictionary<IPAddress, TcpSession> m_Sessions = null;

        private Dictionary<TcpSession, List<byte>> m_Messages = null;

        private List<TcpSession> m_ReceivedMessageClient = null; //正在接收数据的客户端

        private Queue<TcpClient> m_ConnectionQueue = null;

        private Queue<TcpSession> m_ErrorConnectionQueue = null;

        private int mSendTimeOut = 1000;

        private int mReceiveMessageBuffer = 1024;

        /// <summary>
        /// 
        /// </summary>
        public int SendTimeOut
        {
            get
            {
                return mSendTimeOut;
            }
            set
            {
                mSendTimeOut = value;
            }
        }

        /// <summary>
        /// 接受消息缓冲区大小
        /// </summary>
        public int ReceiveMessageBuffer
        {
            get
            {
                return mReceiveMessageBuffer;
            }
            set
            {
                mReceiveMessageBuffer = value;
            }
        }
        
        public override List<SessionBase> Sessions
        {
            get
            {
                List<SessionBase> sessions;
                lock (m_Sessions)
                {
                    sessions = new List<SessionBase>(m_Sessions.Values.ToArray());
                }
                return sessions;
            }
        }

        private TcpServer() : base()
        {
            m_TcpListener = new TcpListener(IPAddress.Any, ServerConfiguration.Port);
            
            m_Sessions = new Dictionary<IPAddress, TcpSession>();
            m_Messages = new Dictionary<TcpSession, List<byte>>();

            m_ReceivedMessageClient = new List<TcpSession>();

            m_ConnectionQueue = new Queue<TcpClient>();
            m_ErrorConnectionQueue = new Queue<TcpSession>();
        }

        /// <summary>
        /// 获取一个服务器实例
        /// </summary>
        /// <returns></returns>
        public static TcpServer GetServer()
        {
            lock (LOCK)
            {
                if (s_TcpServer == null)
                {
                    s_TcpServer = new TcpServer();
                    Log.Logger.LogPath = ServerConfiguration.LogPath;
                    Log.Logger.Open();
                }
                return s_TcpServer;
            }
        }

        /// <summary>
        /// 运行服务器
        /// </summary>
        public override void RunServer()
        {
            try
            {
                GC.Collect();

                m_Sessions.Clear();
                m_ConnectionQueue.Clear();
                m_ErrorConnectionQueue.Clear();
                m_Messages.Clear();

                m_TcpListener.Start();
                m_LastClearDate = DateTime.Now;
                Run = true;
                Parallel.Invoke(AcceptConnection, 
                                ConnectionProcess, 
                                ReceiveMessage, 
                                MessageProcess, 
                                CheckConnectionState, 
                                CheckErrorConnectionState);
            }
            catch (Exception ex)
            {
                Log.Logger.Write(ex.ToString());
            }
        }

        /// <summary>
        /// 接受连接
        /// </summary>
        private void AcceptConnection()
        {
            TcpClient client = null;
            while (Run)
            {
                try
                {
                    client = m_TcpListener.AcceptTcpClient();
                    m_ConnectionQueue.Enqueue(client);
                }
                catch (Exception ex)
                {
                    Log.Logger.Write(ex.ToString());
                    continue;
                }
            }
        }

        /// <summary>
        /// 连接处理
        /// </summary>
        private void ConnectionProcess()
        {
            while (Run)
            {
                try
                {
                    if (m_ConnectionQueue.Count < 1)
                    {
                        Thread.Sleep(1500);
                        continue;
                    }
                    TcpClient client = m_ConnectionQueue.Dequeue();
                    IPAddress clientIP = (client.Client.RemoteEndPoint as IPEndPoint).Address;
                    if (m_Sessions.ContainsKey(clientIP))
                        continue;
                    TcpSession session = new TcpSession();
                    session.UID = Guid.NewGuid();
                    session.Client = client;
                    session.IP = clientIP;
                    session.Client.Client.SendTimeout = mSendTimeOut;
                    m_Sessions.Add(session.IP, session);
                    base.AcceptClientHandler(session);
                }
                catch(Exception ex)
                {
                    Log.Logger.Write(ex.ToString());
                    continue;
                }
            }
        }

        /// <summary>
        /// 接受消息
        /// </summary>
        private void ReceiveMessage()
        {
            byte[] buffer;
            IPAddress[] keys;
            TcpSession session;
            
            while (Run)
            {
                keys = m_Sessions.Keys.ToArray();
                foreach (IPAddress key in keys)
                {
                    if (!m_Sessions.ContainsKey(key))
                        continue;
                    session = m_Sessions[key];
                    try
                    {
                        lock (session)
                        {
                            if (m_ReceivedMessageClient.Contains(session))
                                continue;
                            m_ReceivedMessageClient.Add(session);
                            object[] value = new object[2];
                            buffer = new byte[mReceiveMessageBuffer];
                            value[0] = session;
                            value[1] = buffer;
                            session.Client.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, AyscReceiveMessage, value);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Logger.Write(ex.ToString());
                        m_ErrorConnectionQueue.Enqueue(session);
                        m_Sessions.Remove(key);
                        continue;
                    }
                }
                Thread.Sleep(10);
            }
        }

        private void AyscReceiveMessage(IAsyncResult ai)
        {
            int read;
            object[] objs = ai.AsyncState as object[];
            TcpSession rsession = objs[0] as TcpSession;
            byte[] rbuffer = objs[1] as byte[];
            SocketError error = SocketError.Success;
            read = rsession.Client.Client.EndReceive(ai, out error);
            if (error == SocketError.Success)
            {
                if (read > 0)
                {
                    lock (m_Messages)
                    {
                        if (!m_Messages.ContainsKey(rsession))
                        {
                            m_Messages.Add(rsession, new List<byte>());
                        }
                        m_Messages[rsession].AddRange(rbuffer);
                    }
                }
            }
            m_ReceivedMessageClient.Remove(rsession);
            GC.Collect();
        }

        /// <summary>
        /// 消息处理
        /// </summary>
        private void MessageProcess()
        {
            TcpSession[] keys;
            while (Run)
            {
                try
                {
                    keys = m_Messages.Keys.ToArray();
                    foreach (TcpSession session in keys)
                    {
                        if (!m_Messages.ContainsKey(session) || m_Messages[session].Count < 1)
                            continue;
                        lock (m_Messages)
                        {
                            ReceiveMessageHandler(session, m_Messages[session]);
                            m_Messages.Remove(session);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Logger.Write(ex.ToString());
                }
                Thread.Sleep(10);
            }
        }

        /// <summary>
        /// 检测正常连接的状态
        /// </summary>
        private void CheckConnectionState()
        {
            int count;
            IPAddress[] keys = null;
            while (Run)
            {
                try
                {
                    count = m_Sessions.Count;
                    if (count == 0)
                    {
                        Thread.Sleep(1500);
                        continue;
                    }
                    keys = new IPAddress[count];
                    m_Sessions.Keys.CopyTo(keys, 0);
                    for (int i = 0; i < count; i++)
                    {
                        IPAddress key = keys[i];
                        if (!m_Sessions.ContainsKey(key))
                            continue;
                        TcpSession session = m_Sessions[key];
                        if (session == null)
                            continue;
                        if (IsConnection(session.Client.Client))
                            continue;
                        m_ErrorConnectionQueue.Enqueue(session);
                        m_Sessions.Remove(key);
                    }
                }
                catch (Exception ex)
                {
                    Log.Logger.Write(ex.ToString());
                }
                Thread.Sleep(1000);
            }
        }

        /// <summary>
        /// 检测错误连接的状态
        /// </summary>
        private void CheckErrorConnectionState()
        {
            byte[] testData = new byte[1] { 0 };
            while (Run)
            {
                TcpSession session = null;
                if (m_ErrorConnectionQueue.Count < 1)
                {
                    Thread.Sleep(1500);
                    continue;
                }
                try
                {
                    session = m_ErrorConnectionQueue.Dequeue();
                    session.Client.Client.Send(testData);
                    m_Sessions.Add(session.IP, session);
                }
                catch (Exception ex)
                {
                    Log.Logger.Write(ex.ToString());
                    ClientDownlineHandler(session);
                }
                finally
                {
                    Thread.Sleep(1);
                }
            }
        }

        /// <summary>
        /// 是否连接
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        private bool IsConnection(Socket socket)
        {
            try
            {
                socket.Send(new byte[1] { 0 });
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 发送广播至所有客户端
        /// </summary>
        /// <param name="msg"></param>
        public bool SendBroad(byte[] msg)
        {
            if (msg == null)
                return false;
            Dictionary<IPAddress, TcpSession> sessions = null;
            lock (m_Sessions)
            {
                sessions = new Dictionary<IPAddress, TcpSession>(m_Sessions);
            }
            if (sessions == null || sessions.Count < 1)
                return false;
            foreach (IPAddress ip in sessions.Keys)
            {
                try
                {
                    this.SendTo(sessions[ip], msg);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
            GC.Collect();
            return true;
        }

        /// <summary>
        /// 向指定客户端进行推送消息
        /// </summary>
        /// <param name="session"></param>
        /// <param name="msg"></param>
        public void SendTo(SessionBase session, byte[] msg)
        {
            if (!this.m_Sessions.ContainsKey(session.IP))
                return;
            try
            {
                TcpSession rsession = m_Sessions[session.IP];
                lock (rsession)
                {
                    rsession.Client.Client.SendTo(msg, rsession.Client.Client.RemoteEndPoint);
                }
            }
            catch (Exception ex)
            {
                m_ErrorConnectionQueue.Enqueue(m_Sessions[session.IP]);
                m_Sessions.Remove(session.IP);
                Log.Logger.Write(ex.ToString());
            }
        }

        /// <summary>
        /// 停止服务器
        /// </summary>
        public override void StopServer()
        {
            base.StopServer();
        }

        public override void Dispose()
        {
            Application.Parame.Clear();
        }
    }
}
